package com.js.gate.server.route;

import com.alibaba.fastjson.JSON;
import com.js.common.constant.RedisKeyConstants;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author ygl
 * @create 2018-10-26
 * @DESCRIPTION
 * 自定义动态路由
 **/
@Slf4j
public class MyRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

    @Setter
    private RedisTemplate<String, String> redisTemplate;
    /**
     * 配置文件中的路由信息配置
     */
    private ZuulProperties zuulProperties;

    public MyRouteLocator(String servletPath, ZuulProperties properties) {
        super(servletPath, properties);
        this.zuulProperties = properties;
        log.info("-------servletPath:{}", servletPath);
    }

    @Override
    public void refresh() {
        super.doRefresh();
    }

    /**
     * 定位路由信息
     * @return
     */
    @Override
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
        //从application.properties配置文件中加载路由信息
        routesMap.putAll(super.locateRoutes());
        //从db中加载路由信息
        routesMap.putAll(locateRoutesFrom());

        LinkedHashMap<String, ZuulProperties.ZuulRoute> map = new LinkedHashMap<>();
        for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.zuulProperties.getPrefix())) {
                path = this.zuulProperties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            map.put(path, entry.getValue());
        }

        return map;
    }

    /**
     * 动态获取路由
     * *************可在此修改获取的方式************
     * @return
     */
    private Map<String, ZuulProperties.ZuulRoute> locateRoutesFrom(){
        return locateRoutesFromRedis();
    }
    /**
     * 从redis中获取路由信息
     * @return
     */
    private Map<String, ZuulProperties.ZuulRoute> locateRoutesFromRedis() {
        Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
        List<ZuulRouteVO> results = JSON.parseArray(redisTemplate.opsForValue().get(RedisKeyConstants.ZUUL_ROUTE_KEY), ZuulRouteVO.class);
        for (ZuulRouteVO result : results) {
            if(!result.getEnabled()){
                continue;
            }
            if (StringUtils.isEmpty(result.getPath())) {
                continue;
            }
            if (StringUtils.isEmpty(result.getServiceId()) && StringUtils.isEmpty(result.getUrl())) {
                continue;
            }
            ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
            try {
                BeanUtils.copyProperties(result, zuulRoute);
            } catch (Exception e) {
                log.error("=============load zuul route info from redis with error==============", e);
            }
            routes.put(zuulRoute.getPath(), zuulRoute);
        }
        return routes;
    }

    @ToString
    public static class ZuulRouteVO {

        /**
         * The ID of the route (the same as its map key by default).
         */
        @Getter
        @Setter
        private String id;

        /**
         * The path (pattern) for the route, e.g. /foo/**.
         */
        @Getter
        @Setter
        private String path;

        /**
         * The service ID (if any) to map to this route. You can specify a physical URL or
         * a service, but not both.
         */
        @Getter
        @Setter
        private String serviceId;

        /**
         * A full physical URL to map to the route. An alternative is to use a service ID
         * and service discovery to find the physical address.
         */
        @Getter
        @Setter
        private String url;

        /**
         * Flag to determine whether the prefix for this route (the path, minus pattern
         * patcher) should be stripped before forwarding.
         */
        @Getter
        @Setter
        private Boolean stripPrefix = true;

        /**
         * Flag to indicate that this route should be retryable (if supported). Generally
         * retry requires a service ID and ribbon.
         */
        @Getter
        @Setter
        private Boolean retryable;

        @Getter
        @Setter
        private Boolean enabled;

    }

}
